using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Collections.Generic;


namespace System.Windows.Media.DirectShow.Vorbis
{

    public class IdentificationHeader
    {

        private int version, channels, sampleRate, maximumBitrate, nominalBitrate, minimumBitrate, blockSize0, blockSize1;
        private bool framingFlag;
        private MdctFloat[] mdct = new MdctFloat[2];

        private const long HEADER = 0x736962726f76L; // 'vorbis'

        public IdentificationHeader(VorbisStream vorbis, IBitStream source)
        {
            if (source.ReadInt64(48) != HEADER)
            {
                throw new VorbisFormatException("The identification header has an illegal leading.");
            }
            version = source.ReadInt32(32);
            channels = source.ReadInt32(8);
            sampleRate = source.ReadInt32(32);
            maximumBitrate = source.ReadInt32(32);
            nominalBitrate = source.ReadInt32(32);
            minimumBitrate = source.ReadInt32(32);
            int bs = source.ReadInt32(8);
            blockSize0 = 1 << (bs & 0xf);
            blockSize1 = 1 << (bs >> 4);

            mdct[0] = new MdctFloat(blockSize0);
            mdct[1] = new MdctFloat(blockSize1);
            //mdctInt[0]=new MdctLong(blockSize0);
            //mdctInt[1]=new MdctLong(blockSize1);

            int ff = source.ReadInt32(8);
            framingFlag = ff != 0;
        }

        public MdctFloat Mdct0
        {
            get
            {
                return mdct[0];
            }
        }

        public MdctFloat Mdct1
        {
            get
            {
                return mdct[1];
            }
        }

        public int Channels
        {
            get
            {
                return channels;
            }
        }

        public int SampleRate
        {
            get
            {
                return sampleRate;
            }
        }

        public int NominalBitrate
        {
            get
            {
                return nominalBitrate;
            }
        }

        public int MaximumBitrate
        {
            get
            {
                return maximumBitrate;
            }
        }

        public int MinimumBitrate
        {
            get
            {
                return minimumBitrate;
            }
        }

        public int Version
        {
            get
            {
                return version;
            }
        }

        public int BlockSize0
        {
            get
            {
                return blockSize0;
            }
        }

        public int BlockSize1
        {
            get
            {
                return blockSize1;
            }
        }
    }

    public class CommentHeader
    {

        private const string TITLE = "TITLE";
        private const string ARTIST = "ARTIST";
        private const string ALBUM = "ALBUM";
        private const string TRACKNUMBER = "TRACKNUMBER";
        private const string VERSION = "VERSION";
        private const string PERFORMER = "PERFORMER";
        private const string COPYRIGHT = "COPYRIGHT";
        private const string LICENSE = "LICENSE";
        private const string ORGANIZATION = "ORGANIZATION";
        private const string DESCRIPTION = "DESCRIPTION";
        private const string GENRE = "GENRE";
        private const string DATE = "DATE";
        private const string LOCATION = "LOCATION";
        private const string CONTACT = "CONTACT";
        private const string ISRC = "ISRC";

        private const long HEADER = 0x736962726f76L; // 'vorbis'

        private string vendor;
        private Dictionary<String, List<String>> comments = new Dictionary<String, List<String>>();
        private bool framingBit;

        public CommentHeader(VorbisStream vorbis, IBitStream source)
        {
            if (source.ReadInt64(48) != HEADER)
            {
                throw new VorbisFormatException("The identification header has an illegal leading.");
            }

            vendor = ReadString(source);

            int noc = source.ReadInt32(32);

            for (int i = 0; i < noc; i++)
            {
                string comment = ReadString(source);
                int ix = comment.IndexOf('=');
                string key = comment.Substring(0, ix);
                string value = comment.Substring(ix + 1);
                //comments.put(key, value);
                AddComment(key, value);
            }

            framingBit = source.ReadInt32(8) != 0;
        }

        private string ReadString(IBitStream source)
        {

            int length = source.ReadInt32(32);

            byte[] strArray = new byte[length];

            for (int i = 0; i < length; i++)
            {
                strArray[i] = (byte)source.ReadInt32(8);
            }

            Encoding utf8Enc = new UTF8Encoding();
            char[] chars = new char[utf8Enc.GetCharCount(strArray, 0, length)];
            utf8Enc.GetChars(strArray, 0, length, chars, 0);
            return new String(chars);
        }

        private void AddComment(string key, string value)
        {
            List<String> al = null;

            if (!comments.TryGetValue(key, out al))
            {
                al = new List<String>();
                comments.Add(key.ToUpper(), al);
            }
            al.Add(value);
        }

        public string[] CommentKeys
        {
            get
            {
                ICollection keys = comments.Keys;
                string[] res = new string[keys.Count];
                int cnt = 0;
                foreach (string s in keys)
                {
                    res[cnt++] = s;
                }
                return res;
            }
        }

        public string[] GetComments(String key)
        {
            List<String> al = comments[key.ToUpper()];
            if (al == null)
            {
                return null;
            }
            string[] res = new string[al.Count];
            int cnt = 0;
            foreach (string s in al)
            {
                res[cnt++] = s;
            }
            return res;
        }

        public string GetComment(String key)
        {
            List<String> al = comments[key];
            return al == null ? (String)null : (string)al[0];
        }

        public string Vendor
        {
            get
            {
                return vendor;
            }
        }

        public string[] Titles
        {
            get
            {
                return GetComments(TITLE);
            }
        }

        public string Title
        {
            get
            {
                return GetComment(TITLE);
            }
        }

        public string[] Artists
        {
            get
            {
                return GetComments(ARTIST);
            }
        }

        public string Artist
        {
            get
            {
                return GetComment(ARTIST);
            }
        }

        public string[] Albums
        {
            get
            {
                return GetComments(ALBUM);
            }
        }

        public string Album
        {
            get
            {
                return GetComment(ALBUM);
            }
        }

        public string[] TrackNumbers
        {
            get
            {
                return GetComments(TRACKNUMBER);
            }
        }

        public string TrackNumber
        {
            get
            {
                return GetComment(TRACKNUMBER);
            }
        }

        public string[] Versions
        {
            get
            {
                return GetComments(VERSION);
            }
        }

        public string Version
        {
            get
            {
                return GetComment(VERSION);
            }
        }

        public string[] Performers
        {
            get
            {
                return GetComments(PERFORMER);
            }
        }

        public string Performer
        {
            get
            {
                return GetComment(PERFORMER);
            }
        }

        public string[] Copyrights
        {
            get
            {
                return GetComments(COPYRIGHT);
            }
        }

        public string Copyright
        {
            get
            {
                return GetComment(COPYRIGHT);
            }
        }

        public string[] Licenses
        {
            get
            {
                return GetComments(LICENSE);
            }
        }

        public string Licence
        {
            get
            {
                return GetComment(LICENSE);
            }
        }

        public string[] Organizations
        {
            get
            {
                return GetComments(ORGANIZATION);
            }
        }

        public string Organization
        {
            get
            {
                return GetComment(ORGANIZATION);
            }
        }

        public string[] Descriptions
        {
            get
            {
                return GetComments(DESCRIPTION);
            }
        }

        public string Description
        {
            get
            {
                return GetComment(DESCRIPTION);
            }
        }

        public string[] Genres
        {
            get
            {
                return GetComments(GENRE);
            }
        }

        public string Genre
        {
            get
            {
                return GetComment(GENRE);
            }
        }

        public string[] Dates
        {
            get
            {
                return GetComments(DATE);
            }
        }

        public string Date
        {
            get
            {
                return GetComment(DATE);
            }
        }

        public string[] Locations
        {
            get
            {
                return GetComments(LOCATION);
            }
        }

        public string Location
        {
            get
            {
                return GetComment(LOCATION);
            }
        }

        public string[] Contacts
        {
            get
            {
                return GetComments(CONTACT);
            }
        }

        public string Contact
        {
            get
            {
                return GetComment(CONTACT);
            }
        }

        public string[] Isrcs
        {
            get
            {
                return GetComments(ISRC);
            }
        }

        public string Isrc
        {
            get
            {
                return GetComment(ISRC);
            }
        }
    }

    public class SetupHeader
    {

        private const long HEADER = 0x736962726f76L; // 'vorbis'

        private CodeBook[] codeBooks;
        private Floor[] floors;
        private Residue[] residues;
        private Mapping[] mappings;
        private Mode[] modes;

        public SetupHeader(VorbisStream vorbis, IBitStream source)
        {

            if (source.ReadInt64(48) != HEADER)
            {
                throw new VorbisFormatException("The setup header has an illegal leading.");
            }

            // read code books

            int codeBookCount = source.ReadInt32(8) + 1;
            codeBooks = new CodeBook[codeBookCount];

            for (int i = 0; i < codeBooks.Length; i++)
            {
                codeBooks[i] = new CodeBook(source);
            }

            // read the time domain transformations,
            // these should all be 0

            int timeCount = source.ReadInt32(6) + 1;

            for (int i = 0; i < timeCount; i++)
            {
                if (source.ReadInt32(16) != 0)
                {
                    throw new VorbisFormatException("Time domain transformation != 0");
                }
            }

            // read floor entries

            int floorCount = source.ReadInt32(6) + 1;
            floors = new Floor[floorCount];

            for (int i = 0; i < floorCount; i++)
            {
                floors[i] = Floor.CreateInstance(source, this);
            }

            // read residue entries

            int residueCount = source.ReadInt32(6) + 1;
            residues = new Residue[residueCount];

            for (int i = 0; i < residueCount; i++)
            {
                residues[i] = Residue.CreateInstance(source, this);
            }

            // read mapping entries

            int mappingCount = source.ReadInt32(6) + 1;
            mappings = new Mapping[mappingCount];

            for (int i = 0; i < mappingCount; i++)
            {
                mappings[i] = Mapping.CreateInstance(vorbis, source, this);
            }

            // read mode entries

            int modeCount = source.ReadInt32(6) + 1;
            modes = new Mode[modeCount];

            for (int i = 0; i < modeCount; i++)
            {
                modes[i] = new Mode(source, this);
            }

            if (!source.ReadBit())
            {
                throw new VorbisFormatException("The setup header framing bit is incorrect.");
            }
        }

        public CodeBook[] CodeBooks
        {
            get
            {
                return codeBooks;
            }
        }

        public Floor[] Floors
        {
            get
            {
                return floors;
            }

        }

        public Residue[] Residues
        {
            get
            {
                return residues;
            }
        }

        public Mapping[] Mappings
        {
            get
            {
                return mappings;
            }
        }

        public Mode[] Modes
        {
            get
            {
                return modes;
            }
        }

    }




}